package com.winbomb.simulife.impl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.util.Log;

import com.winbomb.simulife.World;
import com.winbomb.simulife.base.IEvent;
import com.winbomb.simulife.base.IEventManager;

public class EventManager implements IEventManager {

	private static final String TAG = "EventManager";

	private static World world;
	private static EventManager instance = null;
	private static final String CONFIG_FILE_PATH = "/com/winbomb/simulife/impl/events/config.xml";
	private static final String CLASS_NAME_PREFIX = "com.winbomb.simulife.impl.events.";

	/** 所有定义的Event */
	private static IEvent[] allEvents;

	private EventManager(World paramWorld) {

		world = paramWorld;

		SAXParserFactory factory = SAXParserFactory.newInstance();
		try {
			SAXParser parser = factory.newSAXParser();
			InputStream fis = EventManager.class.getResourceAsStream(CONFIG_FILE_PATH);

			XMLContentHandler handler = new XMLContentHandler();
			parser.parse(fis, handler);

			EventElem[] eventElems = handler.getEvents();

			allEvents = parseEventElems(eventElems);

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}

	@SuppressWarnings("unchecked")
	private IEvent[] parseEventElems(EventElem[] events) {

		List<IEvent> eventList = new ArrayList<IEvent>();
		for (EventElem event : events) {
			if (event == null) {
				continue;
			}

			String classFullName = CLASS_NAME_PREFIX + event.getClassName();
			Class<World> clazz;
			try {
				clazz = (Class<World>) Class.forName(classFullName);
				Constructor<World> constructor = clazz.getConstructor(World.class);
				IEvent eventImpl = (IEvent) constructor.newInstance(world);
				eventList.add(eventImpl);
			} catch (ClassNotFoundException e) {
				Log.w(TAG, "Could not found class: " + classFullName);
			} catch (NoSuchMethodException e) {
				Log.w(TAG, "Cound not find proper constructor while loading class: "
						+ classFullName);
			} catch (Exception ex) {
				Log.w(TAG, "Error occured while loading class: " + classFullName + " caused by "
						+ ex);
			}

		}

		return eventList.toArray(new IEvent[eventList.size()]);

	}

	public synchronized static EventManager getInstance(World paramWorld) {

		if (instance == null) {
			instance = new EventManager(paramWorld);
		}

		return instance;
	}

	@Override
	public List<IEvent> fireNewEvents() {

		if (allEvents == null || allEvents.length == 0) {
			Log.e("EventManager", "Loading events failed, event list is null.");
		}

		List<IEvent> events = new ArrayList<IEvent>();

		for (IEvent event : allEvents) {
			if (event.isEnable()) {
				events.add(event);
			}
		}

		return events;
	}

	/*
	 * <p>SAX是一个解析速度快并且占用内存少的xml解析器，非常适合用于Android等移动设备。
	 * SAX解析XML文件采用的是事件驱动，也就是说，它并不需要解析完整个文档 ，在按内容顺序解析
	 * 文档的过程中，SAX会判断当前读到的字符是否合法XML语法中的某部分 ，如果符合就会触发事件。
	 * 所谓事件，其实就是一些回调（callback）方法，这些方法(事件)定义在ContentHandler接口。
	 * 下面是一些ContentHandler接口常用的方法：</p> <p>startDocument()
	 * 当遇到文档的开头的时候，调用这个方法，可以在其中做一些预处理的工作。 endDocument()
	 * 和上面的方法相对应，当文档结束的时候，调用这个方法，可以在其中做一 些善后的工作。 startElement(String
	 * namespaceURI, String localName, String qName, Attributes atts)
	 * 当读到一个开始标签的时候
	 * ，会触发这个方法。namespaceURI就是命名空间，localName是不带命名空间前缀的标签名，qName是带命名空间前缀的标签名
	 * 。通过atts可以得到所有的属性名和相应的值
	 * 。要注意的是SAX中一个重要的特点就是它的流式处理，当遇到一个标签的时候，它并不会纪录下以前所碰到的标签
	 * ，也就是说，在startElement()方法中
	 * ，所有你所知道的信息，就是标签的名字和属性，至于标签的嵌套结构，上层标签的名字，是否有子元属等等其它与结构相关的信息
	 * ，都是不得而知的，都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。 endElement(String uri,
	 * String localName, String name) 这个方法和上面的方法相对应，在遇到结束标签的时候，调用这个方法。
	 * characters(char[] ch, int start, int length)
	 * 这个方法用来处理在XML文件中读到的内容，第一个参数为文件的字符串内容，后面两个参数是读到的字符串在这个数组中的起始位置和长度，使用new
	 * String(ch,start,length)就可以获取内容。
	 */
	class XMLContentHandler extends DefaultHandler {

		private List<EventElem> eventList;
		private EventElem event;
		private String preTag;
		private static final String ELEM_EVENT = "Event";
		private static final String ELEM_CLASS_NAME = "ClassName";
		private static final String ELEM_PICTURE = "Picture";
		private static final String ELEM_DESCRIPTION = "Description";

		public EventElem[] getEvents() {
			return eventList.toArray(new EventElem[eventList.size()]);
		}

		/**
		 * 要开始读取xml文件的时候调用的方法 初始化persons
		 */
		@Override
		public void startDocument() throws SAXException {
			eventList = new ArrayList<EventElem>();
		}

		/**
		 * sax 读取到文本节点的时候调用了这个方法
		 */
		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {

			if (preTag != null && event != null) {
				String data = new String(ch, start, length);
				if (ELEM_CLASS_NAME.equals(preTag)) {
					event.setClassName(data);
				} else if (ELEM_PICTURE.equals(preTag)) {
					event.setPictureName(data);
				} else if (ELEM_DESCRIPTION.equals(preTag)) {
					event.setDescription(data);
				}
			}
		}

		/**
		 * sax 读取到元素节点的时候用到这个方法；
		 */
		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes)
				throws SAXException {
			if (ELEM_EVENT.equals(localName)) {
				event = new EventElem();
			}
			preTag = localName;
		}

		/**
		 * 这个方法是每次遇到结束的标签都会执行的 并不是只遇到最后的结尾才调用
		 */

		@Override
		public void endElement(String uri, String localName, String name) throws SAXException {

			if (ELEM_EVENT.equals(localName) && event != null) {
				eventList.add(event);
				event = null;
			}
			preTag = null;
		}

	}
}

class EventElem {

	String className;
	String pictureName;
	String description;

	public String toString() {
		StringBuffer sb = new StringBuffer();

		sb.append("ClassName: ");
		sb.append(className);
		sb.append("   PictureName: ");
		sb.append(pictureName);
		sb.append("   Description: ");
		sb.append(description);

		return sb.toString();
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String getPictureName() {
		return pictureName;
	}

	public void setPictureName(String pictureName) {
		this.pictureName = pictureName;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
}
